package com.gitee.wsl.transform.encode.hex

import com.gitee.wsl.ext.base.SPACE
import com.gitee.wsl.ext.string.EMPTY
import com.gitee.wsl.transform.encode.base.Base16
import com.ionspin.kotlin.bignum.integer.BigInteger


@OptIn(ExperimentalStdlibApi::class)
internal object HexUtil {
    private var NEWLINE = "\n"
    private var EMPTY_STRING = ""

    private val BYTE2CHAR = CharArray(256)
    private val HEXDUMP_TABLE = CharArray(256 * 4)
    private val HEXPADDING = arrayOfNulls<String>(16)
    private val HEXDUMP_ROWPREFIXES = arrayOfNulls<String>(65536 ushr 4)
    private val BYTE2HEX = arrayOfNulls<String>(256)
    private val BYTEPADDING = arrayOfNulls<String>(16)
    private val BYTE2HEX_PAD = arrayOfNulls<String>(256)

    init {
        // Generate the lookup table that converts a byte into a 2-digit hexadecimal integer.
        for (i in BYTE2HEX_PAD.indices) {
            val str = i.toHexString()
            BYTE2HEX_PAD[i] = if (i > 0xf) str else "0$str"
        }

        val DIGITS = "0123456789abcdef".toCharArray()
        for (i in 0..255) {
            HEXDUMP_TABLE[i shl 1] = DIGITS[i ushr 4 and 0x0F]
            HEXDUMP_TABLE[(i shl 1) + 1] = DIGITS[i and 0x0F]
        }

        // Generate the lookup table for hex dump paddings
        var i: Int = 0
        while (i < HEXPADDING.size) {
            val padding = HEXPADDING.size - i
            val buf = StringBuilder(padding * 3)
            for (j in 0 until padding) {
                buf.append("   ")
            }
            HEXPADDING[i] = buf.toString()
            i++
        }

        // Generate the lookup table for the start-offset header in each row (up to 64KiB).
        i = 0
        while (i < HEXDUMP_ROWPREFIXES.size) {
            val buf = StringBuilder(12)
            buf.append(NEWLINE)
            buf.append(((i shl 4).toLong() and 0xFFFFFFFFL or 0x100000000L).toHexString())
            buf[buf.length - 9] = '|'
            buf.append('|')
            HEXDUMP_ROWPREFIXES[i] = buf.toString()
            i++
        }

        // Generate the lookup table for byte-to-hex-dump conversion
        i = 0
        while (i < BYTE2HEX.size) {
            BYTE2HEX[i] = ' '.toString() + BYTE2HEX_PAD[i and 0xff]
            i++
        }



        // Generate the lookup table for byte dump paddings
        i = 0
        while (i < BYTEPADDING.size) {
            val padding = BYTEPADDING.size - i
            val buf = StringBuilder(padding)
            for (j in 0 until padding) {
                buf.append(' ')
            }
            BYTEPADDING[i] = buf.toString()
            i++
        }

        // Generate the lookup table for byte-to-char conversion
        i = 0
        while (i < BYTE2CHAR.size) {
            if (i <= 0x1f || i >= 0x7f) {
                BYTE2CHAR[i] = '.'
            }
            else {
                BYTE2CHAR[i] = i.toChar()
            }
            i++
        }
    }

    fun hexDump(array: ByteArray, fromIndex: Int, length: Int): String {
        // checkPositiveOrZero(length, "length");
        if (length == 0) {
            return ""
        }
        val endIndex = fromIndex + length
        val buf = CharArray(length shl 1)
        var srcIdx = fromIndex
        var dstIdx = 0
        while (srcIdx < endIndex) {
            /*System.arraycopy(
                HEXDUMP_TABLE, array[srcIdx].toInt() and 0xFF shl 1, buf, dstIdx, 2
            )*/
            HEXDUMP_TABLE.copyInto(buf,dstIdx,array[srcIdx].toInt() and 0xFF shl 1,2)
            srcIdx++
            dstIdx += 2
        }
        return buf.concatToString()
    }

    fun prettyHexDump(array: ByteArray, fromIndex: Int, length: Int): String {
        return if (length == 0) {
            EMPTY_STRING
        }
        else {
            val rows = length / 16 + (if (length and 15 == 0) 0 else 1) + 4
            val buf = StringBuilder(rows * 80)
            appendPrettyHexDump(buf, array, fromIndex, length)
            buf.toString()
        }
    }

    private fun appendPrettyHexDump(dump: StringBuilder, array: ByteArray, fromIndex: Int, length: Int) {
        require(fromIndex in 0..length) { "expected: 0 <= fromIndex($fromIndex) <= length($length)" }

        if (length == 0) {
            return
        }
        dump.append(
            "         +-------------------------------------------------+" + NEWLINE +
                    "         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |" + NEWLINE +
                    "+--------+-------------------------------------------------+----------------+"
        )

        val fullRows = length ushr 4
        val remainder = length and 0xF

        // Dump the rows which have 16 bytes.
        for (row in 0 until fullRows) {
            val rowStartIndex = (row shl 4) + fromIndex

            // Per-row prefix.
            appendHexDumpRowPrefix(dump, row, rowStartIndex)

            // Hex dump
            val rowEndIndex = rowStartIndex + 16
            for (j in rowStartIndex until rowEndIndex) {
                dump.append(BYTE2HEX[(array[j].toInt() and 0xFF).toShort().toInt()])
            }
            dump.append(" |")

            // ASCII dump
            for (j in rowStartIndex until rowEndIndex) {
                dump.append(BYTE2CHAR[(array[j].toInt() and 0xFF).toShort().toInt()])
            }
            dump.append('|')
        }

        // Dump the last row which has less than 16 bytes.
        if (remainder != 0) {
            val rowStartIndex = (fullRows shl 4) + fromIndex
            appendHexDumpRowPrefix(dump, fullRows, rowStartIndex)

            // Hex dump
            val rowEndIndex = rowStartIndex + remainder
            for (j in rowStartIndex until rowEndIndex) {
                dump.append(BYTE2HEX[(array[j].toInt() and 0xFF).toShort().toInt()])
            }
            dump.append(HEXPADDING[remainder])
            dump.append(" |")

            // Ascii dump
            for (j in rowStartIndex until rowEndIndex) {
                dump.append(BYTE2CHAR[(array[j].toInt() and 0xFF).toShort().toInt()])
            }
            dump.append(BYTEPADDING[remainder])
            dump.append('|')
        }
        dump.append(
            "$NEWLINE+--------+-------------------------------------------------+----------------+"
        )
    }

    private fun appendHexDumpRowPrefix(dump: StringBuilder, row: Int, rowStartIndex: Int) {
        if (row < HEXDUMP_ROWPREFIXES.size) {
            dump.append(HEXDUMP_ROWPREFIXES[row])
        }
        else {
            dump.append(NEWLINE)
            dump.append((rowStartIndex.toLong() and 0xFFFFFFFFL or 0x100000000L).toHexString())
            dump[dump.length - 9] = '|'
            dump.append('|')
        }
    }

    /**
     * 判断给定字符串是否为16进制数<br></br>
     * 如果是，需要使用对应数字类型对象的`decode`方法解码<br></br>
     * 例如：`Integer.decode`方法解码int类型的16进制数字
     *
     * @param value 值
     * @return 是否为16进制
     */
    fun isHexNumber(value: String): Boolean {
        if (value.startsWith( "-")) {
            // issue#2875
            return false
        }
        var index = 0
        if (value.startsWith("0x", index) || value.startsWith("0X", index)) {
            index += 2
        } else if (value.startsWith("#", index)) {
            index++
        }
        try {
            BigInteger.parseString(value.substring(index), 16)
        } catch (e: NumberFormatException) {
            return false
        }
        return true
    }

    /**
     * 将指定int值转换为Unicode字符串形式，常用于特殊字符（例如汉字）转Unicode形式<br></br>
     * 转换的字符串如果u后不足4位，则前面用0填充，例如：
     *
     * <pre>
     * '你' =》\u4f60
    </pre> *
     *
     * @param value int值，也可以是char
     * @return Unicode表现形式
     */
    fun toUnicodeHex(value: Int): String {
        val builder = StringBuilder(6)

        builder.append("\\u")
        val hex: String = value.toHexString()
        val len = hex.length
        if (len < 4) {
            builder.append("0000", 0, 4 - len) // 不足4位补0
        }
        builder.append(hex)

        return builder.toString()
    }

    /**
     * 将指定char值转换为Unicode字符串形式，常用于特殊字符（例如汉字）转Unicode形式<br></br>
     * 转换的字符串如果u后不足4位，则前面用0填充，例如：
     *
     * <pre>
     * '你' =》'\u4f60'
    </pre> *
     *
     * @param ch char值
     * @return Unicode表现形式
     * @since 4.0.1
     */
    fun toUnicodeHex(ch: Char): String {
        return Base16.toUnicodeHex(ch)
    }



    /**
     * 格式化Hex字符串，结果为每2位加一个空格，类似于：
     * <pre>
     * e8 8c 67 03 80 cb 22 00 95 26 8f
    </pre> *
     *
     * @param hexStr Hex字符串
     * @param prefix 自定义前缀，如0x
     * @return 格式化后的字符串
     */
    fun format(hexStr: String, prefix: String = String.EMPTY): String {
        val length = hexStr.length
        val builder = StringBuilder(length + length / 2 + (length / 2 * prefix.length))
        builder.append(prefix).append(hexStr[0]).append(hexStr[1])
        var i = 2
        while (i < length - 1) {
            builder.append(Char.SPACE).append(prefix).append(hexStr[i]).append(hexStr[i + 1])
            i += 2
        }
        return builder.toString()
    }
}


